# -*- coding: utf-8 -*-
"""
Created on Wed Apr 18 08:23:47 2018

@author: mojm
"""
import numpy as np
import sklearn.datasets
import matplotlib.pyplot as plt
from sklearn import tree

from sklearn.feature_extraction import DictVectorizer

def plot_decision_boundary(pred_func):  
    # 设定最大最小值，附加一点点边缘填充  
    x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5  # JM: 获取makemoon生成出来的数据中的一个切面作为x轴
    y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5  # JM: 获取makemoon生成出来的数据中的二个切面作为y轴
    h = 0.01  # JM: 表示以0.01的步进绘制网格
  
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))  # numpy.meshgrid()——生成网格点坐标矩阵。
  
    # 用预测函数预测一下  
    # JM: np.c_ 是把矩阵或者向量按照第二个轴排起来.也就是列轴. 
    # JM：np.c_ 就是再次把xx.ravel()的一维数组和yy.ravel()的一维数组排列成一个二维数组
    # JM: xx.ravel():表示把一个矩阵行优先展成一个向量.跟flatten一样. 
    # JM: numpy.flatten()返回一份拷贝，而numpy.ravel()返回的是视图，会影响（reflects）原始矩阵。
    Z = pred_func(np.c_[xx.ravel(), yy.ravel()]) # JM: 这里的操作等于是把网格坐标矩阵的点都预测了一遍，然后填充上蓝红两类代表的颜色，最后再在上面画样本点
    Z = Z.reshape(xx.shape)  # JM: 按照xx.shape调整形状
  
    # 然后画出图  
    # contour和contourf都是画三维等高线图的
    # 不同点在于contour() 是绘制轮廓线，contourf()会填充轮廓
    # Z中的数据便是1或者0，表示分类，在plt.contourf中Z表示为等高线的高度值
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)  
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral) 

n_samples=1000

X, y = sklearn.datasets.make_moons(n_samples, noise=0.20)

sampleRatio = 0.9
targetRatio = round(1-sampleRatio, 2)
sampleBoundary = int(n_samples*sampleRatio)

training_features = X[:sampleBoundary]
training_targets = y[:sampleBoundary]

test_features = X[sampleBoundary:]
test_targets = y[sampleBoundary:]

'''
比较重要的参数：
criterion ：规定了该决策树所采用的的最佳分割属性的判决方法，有两种：“gini”，“entropy”。 
max_depth ：限定了决策树的最大深度，对于防止过拟合非常有用。
min_samples_leaf ：限定了叶子节点包含的最小样本数，这个属性对于防止上文讲到的数据碎片问题很有作用 
模块中一些重要的属性方法： 
n_classes_ ：决策树中的类数量。 
classes_ ：返回决策树中的所有种类标签。 
feature_importances_ ：feature的重要性，值越大那么越重要。
fit(X, y, sample_mask=None, X_argsorted=None, check_input=True, sample_weight=None) 将数据集x，和标签集y送入分类器进行训练，这里要注意一个参数是：sample_weright，它和样本的数量一样长，所携带的是每个样本的权重。 
get_params(deep=True) 得到决策树的各个参数。 
set_params(**params) 调整决策树的各个参数。 
predict(X) 送入样本X，得到决策树的预测。可以同时送入多个样本。 
transform(X, threshold=None) 返回X的较重要的一些feature，相当于裁剪数据。 
score(X, y, sample_weight=None) 返回在数据集X,y上的测试分数，正确率。
使用建议 
1. 当我们数据中的feature较多时，一定要有足够的数据量来支撑我们的算法，不然的话很容易overfitting 
2. PCA是一种避免高维数据overfitting的办法。
3. 从一棵较小的树开始探索，用export方法打印出来看看。 
4. 善用max_depth参数，缓慢的增加并测试模型，找出最好的那个depth。
5. 善用min_samples_split和min_samples_leaf参数来控制叶子节点的样本数量，防止overfitting。 
6. 平衡训练数据中的各个种类的数据，防止一个种类的数据dominate。
'''

clf = tree.DecisionTreeRegressor(max_depth=6)
clf = clf.fit(training_features, training_targets)

plot_decision_boundary(lambda x: clf.predict(x))
#plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)

predict_targets = clf.predict(test_features)

rounded_predict_targets = [round(x) for x in predict_targets]
accuracy = sklearn.metrics.accuracy_score(test_targets, rounded_predict_targets)

#print clf.decision_path()
print ('accuracy is ...' + str(accuracy))

# Vetorize features
# vec = DictVectorizer()
# dummyX = vec.fit_transform(featureList).toarray()
with open(r'D:\tree.dot','w+') as f:
    f=tree.export_graphviz(clf,out_file=f)
# with open("allElectronicInformationGainOri.dot", 'w') as f:
#    f = tree.export_graphviz(clf, feature_names = vec.get_feature_names(), out_file = f)